use crate::{
    interrupt::Code::{
        self, MachineExternal, MachineSoftware, MachineTimer, SupervisorExternal,
        SupervisorSoftware, SupervisorTimer,
    },
    {csrc, csrci, csrr, csrs, csrsi, csrw},
};

use core::marker::PhantomData;

use bits::{BitsOps, IntoBits};

pub struct Cache {
    pub ssip: bool,
    pub msip: bool,
    pub stip: bool,
    pub mtip: bool,
    pub seip: bool,
    pub meip: bool,
    phantom: PhantomData<usize>,
}

impl From<usize> for Cache {
    fn from(raw: usize) -> Self {
        Cache {
            ssip: raw.bits(SupervisorSoftware as u32).is_set(),
            msip: raw.bits(MachineSoftware as u32).is_set(),
            stip: raw.bits(SupervisorTimer as u32).is_set(),
            mtip: raw.bits(MachineTimer as u32).is_set(),
            seip: raw.bits(SupervisorExternal as u32).is_set(),
            meip: raw.bits(MachineExternal as u32).is_set(),
            phantom: PhantomData,
        }
    }
}
impl Into<usize> for Cache {
    fn into(self) -> usize {
        let ret = 0usize;
        ret.bits(SupervisorSoftware as u32)
            .write(self.ssip as usize)
            .bits(MachineSoftware as u32)
            .write(self.msip as usize)
            .bits(SupervisorTimer as u32)
            .write(self.stip as usize)
            .bits(MachineTimer as u32)
            .write(self.mtip as usize)
            .bits(SupervisorExternal as u32)
            .write(self.seip as usize)
            .bits(MachineExternal as u32)
            .write(self.meip as usize)
    }
}
impl Cache {
    pub fn flush(self) {
        let reg_val: usize = self.into();
        csrw!(mip, reg_val)
    }
}

pub fn cache() -> Cache {
    let raw = csrr!(mip);
    raw.into()
}
pub fn ssip(enable: bool) {
    if enable {
        csrsi!(mip, 0b10);
    } else {
        csrci!(mip, 0b10);
    }
}
pub fn msip(enable: bool) {
    if enable {
        csrsi!(mip, 0b1000);
    } else {
        csrci!(mip, 0b1000);
    }
}
fn cfg_by_pos(enable: bool, pos: Code) {
    let mask = 1usize << (pos as usize);
    if enable {
        csrs!(mip, mask);
    } else {
        csrc!(mip, mask);
    }
}
pub fn stip(enable: bool) {
    cfg_by_pos(enable, SupervisorTimer);
}
pub fn mtip(enable: bool) {
    cfg_by_pos(enable, MachineTimer);
}
pub fn seip(enable: bool) {
    cfg_by_pos(enable, SupervisorExternal);
}
/// 配置中断使能
pub fn meip(enable: bool) {
    cfg_by_pos(enable, MachineExternal);
}
